/*********************************
 * PROPRIETARY/CONFIDENTIAL.  Use of this product is subject to license terms.
 * Copyright (c) 2012 NVision Group, Inc. All rights reserved.
 *
 * CallbackMask.java 03.07.2012 18:50:19
 *********************************/
package ru.kruzhki.client.rpc;

import java.util.Map;

import ru.kruzhki.client.gwt.Maskable;

import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.shared.FastMap;
import com.sencha.gxt.widget.core.client.Component;

/**
 * @author AKovalyov
 *
 */
public abstract class MaskCallback<T> extends AbstractCallback<T> {

    private static Map<String, Integer> keyMap = new FastMap<Integer>();
    
    public enum UnMaskMode {
        BEFORE, AFTER, NONE
    }        

    private Maskable maskHolder;
    private Component[] maskComponents;
    private XElement[] maskElements;
    private UnMaskMode mode;
    private boolean canUnmask = true;
    private String сkey;
    private Integer cId;

    public MaskCallback(Component... maskComponents) {
        this((String) null, UnMaskMode.AFTER, maskComponents);
    }
    
    public MaskCallback(XElement... maskElements) {
        this((String) null, UnMaskMode.AFTER, maskElements);
    }    

    public MaskCallback(Maskable maskHolder) {
        this((String) null, maskHolder);
    }

    public MaskCallback(String maskText, Component... maskComponents) {
        this(maskText, UnMaskMode.AFTER, maskComponents);
    }
    
    public MaskCallback(String validKey, String maskText, Maskable maskHolder) {
        this(validKey, maskText, UnMaskMode.AFTER, maskHolder);
    }

    public MaskCallback(String maskText, Maskable maskHolder) {
        this(null, maskText, UnMaskMode.AFTER, maskHolder);
    }

    public MaskCallback(UnMaskMode mode, Component... maskComponents) {
        this((String) null, mode, maskComponents);
    }

    public MaskCallback(Maskable maskHolder, UnMaskMode mode) {
        this((String) null, (String) null, mode, maskHolder);
    }

    private MaskCallback(String maskText, UnMaskMode mode, Component... maskComponents) {
        super();

        if (maskComponents == null || maskComponents.length == 0) {
            throw new IllegalArgumentException("Mask component must not be null");
        }

        this.maskComponents = maskComponents;
        this.mode = mode;

        for (Component maskComponent : maskComponents) {
            maskComponent.mask(maskText);
        }
    }
    
    private MaskCallback(String maskText, UnMaskMode mode, XElement... maskElements) {
        super();

        if (maskElements == null || maskElements.length == 0) {
            throw new IllegalArgumentException("Mask component must not be null");
        }

        this.maskElements = maskElements;
        this.mode = mode;

        for (XElement element : maskElements) {
            element.mask(maskText);
        }
    }

    private MaskCallback(String validKey, String maskText, UnMaskMode mode, Maskable maskHolder) {
        super();

        if (maskHolder == null) {
            throw new IllegalArgumentException("Mask holder must not be null");
        }
        
        register(validKey);

        this.maskHolder = maskHolder;
        this.mode = mode;

        if (maskText == null) {
            maskHolder.mask();
            
        } else {
            maskHolder.mask(maskText);
        }
    }
    
    private void register(String key) {
        if (key != null) {
            Integer id = keyMap.get(key);
            id = id != null ? ++id : 1;// (: код такой за рай в ты попадешь не ,Шурик ,Ой
            keyMap.put(key, id);

            this.сkey = key;
            this.cId = id;            
        }
    }
    
    private boolean isValid() {
        return сkey != null ? keyMap.get(сkey).equals(cId) : true;
    }

    @Override
    public final void onFailure(Throwable caught) {
        unmusk();
        
        if (isValid()) {
            onFailureInternal(caught);   
        }        
    }

    @Override
    public final void onSuccess(T result) {
        try {
            if (mode == UnMaskMode.BEFORE) {
                unmusk();
            }

            if (isValid()) {
                onSuccessInternal(result);
                
            } else {
                onCancelInternal(result);
            }

        } catch (Throwable e) {
            onFailure(e);

        } finally {
            if (mode == UnMaskMode.AFTER) {
                unmusk();
            }
        }
    }

    public boolean canUnmask() {
        return canUnmask && isValid();
    }
    
    public void setCanUnmask(boolean canUnmask) {
        this.canUnmask = canUnmask;
    }

    private void unmusk() {
        if (!canUnmask()) {
            return;
        }
        
        if (maskComponents != null) {
            for (Component maskComponent : maskComponents) {
                maskComponent.unmask();
            }
        }
        
        if (maskElements != null) {
            for (XElement element : maskElements) {
                element.unmask();
            }
        }

        if (maskHolder != null) {
            maskHolder.unmask();
        }
    }
    
    protected void onFailureInternal(Throwable caught) {
        super.onFailure(caught);
    }

    protected abstract void onSuccessInternal(T result);
    
    @SuppressWarnings("unused")
    protected void onCancelInternal(T result) {
        //пустая имплементация        
    }
    
}
